/**
 *
 *embustest remote command.
 *
 * @remote_cmd.cpp
 *
 * $Author: ycl $
 * $Date: 2023-7-14 $
 *
 * @remarks
 * 
 */

#include "msgproctest.h"
#include "cJSON.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <future>
#include "emlog.hpp"

#define EMRC_MAXBUF 20000
#define EMRC_UDP_PORT 12000

typedef struct _tEMRCInfo
{
    embus_handle_t* embusHandle;
    int sock; //udp socket
    char buf[EMRC_MAXBUF];
    struct sockaddr_in client_addr;
    struct sockaddr_in server_addr;
    int addr_size;
    bool bCandump_subscribe;
    bool bLindump_subscribe;
}tEMRCInfo;

tEMRCInfo gEMRCInfo = {0};
EMLog emlog("EMRC");

int emrc_createSocket()
{    
    gEMRCInfo.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    memset(&gEMRCInfo.server_addr, 0, gEMRCInfo.addr_size);     
    gEMRCInfo.server_addr.sin_family = AF_INET;
    gEMRCInfo.server_addr.sin_addr.s_addr = INADDR_ANY;
    gEMRCInfo.server_addr.sin_port = htons(EMRC_UDP_PORT);
    bind(gEMRCInfo.sock, (struct sockaddr *)&gEMRCInfo.server_addr, sizeof(struct sockaddr));    
    return 0;
}

int cmdd_cansend(string channel, int loop, int interval, string frame)
{
    std::future<int>  f_ret;
    emlog.Info("cmdd_cansend(channel=%s, loop=%d, interval=%d, frame=%s)\n", channel.c_str(),loop,interval,frame.c_str());
    f_ret = std::async(std::launch::async,[=](){
               return msg_tx_canmsg_test(gEMRCInfo.embusHandle, channel, loop, interval, frame);
            });               
    return 0;
}

int emrc_jsonParse(char* json_str)
{
    cJSON *monitor = cJSON_Parse(json_str);
    if(NULL == monitor) return -1;

    cJSON *cmd = cJSON_GetObjectItemCaseSensitive(monitor, "cmd");
    if(cJSON_IsString(cmd) && (cmd->valuestring != NULL))
    {        
        if(0 == strcmp(cmd->valuestring, "cansend"))
        {
            cJSON *paras = cJSON_GetObjectItemCaseSensitive(monitor, "paras");
            if(NULL == paras) return -2;
            cJSON *channel = cJSON_GetObjectItemCaseSensitive(paras, "channel");
            cJSON *loop = cJSON_GetObjectItemCaseSensitive(paras, "loop");
            cJSON *interval = cJSON_GetObjectItemCaseSensitive(paras, "interval");
            cJSON *frame = cJSON_GetObjectItemCaseSensitive(paras, "frame");
            if(!cJSON_IsString(channel) || NULL == channel->valuestring ) return -3;
            if(!cJSON_IsString(frame) || NULL == frame->valuestring ) return -4;
            if(!cJSON_IsNumber(loop) || loop->valueint < 0) return -5;
            if(!cJSON_IsNumber(interval) || interval->valueint < 0) return -6;
            cmdd_cansend(channel->valuestring, loop->valueint, interval->valueint, frame->valuestring);
        }
        else if(0 == strcmp(cmd->valuestring, "candump_subscribe"))
        {
            gEMRCInfo.bCandump_subscribe = true;
        }
        else if(0 == strcmp(cmd->valuestring, "lindump_subscribe"))
        {
            gEMRCInfo.bLindump_subscribe = true;
        }
    }
    
    return 0;
}

/**
********************************************************************************
 * @name  EMRC_run
 * @deprecated  EMRC_run entrance
 * @param handle      [in] embus handle.
 * @param para        [in] para：0- no debug output, 1- debug output
 * @return  send message flag
 * @remarks
 ********************************************************************************
*/
int EMRC_run(embus_handle_t* handle, int para)
{
    int cc;
    gEMRCInfo.embusHandle = handle;
    gEMRCInfo.addr_size = sizeof(gEMRCInfo.client_addr);
    if(1 == para) emlog.SetLevel(emlog.LogLevelDebug);
    emrc_createSocket();
    emlog.Info("The em remote cmd server is ready-----------------------");
    while (1)
    {        
        cc = recvfrom(gEMRCInfo.sock, gEMRCInfo.buf, EMRC_MAXBUF, 0, 
            (struct sockaddr *)&gEMRCInfo.client_addr, (socklen_t*)&gEMRCInfo.addr_size);        
        // 输出client     
        emlog.Info("Recv from client(%s:%d).",inet_ntoa(gEMRCInfo.client_addr.sin_addr),ntohs(gEMRCInfo.client_addr.sin_port));
        // 输出用户传来的信息
        if(cc >= EMRC_MAXBUF){
            emlog.Warn("The message length out of range: %d", cc);
            continue;
        }
        gEMRCInfo.buf[cc] = '\0';
        emlog.Debug("Recv message : %s\n", gEMRCInfo.buf);            
        emrc_jsonParse(gEMRCInfo.buf);
        emlog.Debug("End-----------------------");        
    }
}

#define EMRC_DIR_RX  0
#define EMRC_DIR_TX  1
void EMRC_msgProc(unsigned int pkt_id, unsigned char *data, int data_len)
{
    uint16_t ch;
    uint32_t id;
    int direction;
    int type;
    int dlc;
    char da[128] = {0};    

    if (pkt_id == EMBUS_BMR_DAT_ID)
    {
        struct com_packet* pac = (struct com_packet*)data;
        if ((pac->id != 0) && (pac->id != 0xFFFFFFFF) && (pac->id != 0XFFFFFFFE) && (data_len == sizeof(struct com_packet)))
        {
            ch = pac->channel & 0x0F;
            id = pac->id & 0x0FFFFFFF;
            type = (pac->channel & 0xC0) >> 6;
            dlc = pac->dlc;
            direction = (((pac->channel & 0x20) >> 5) == 0) ? EMRC_DIR_RX : EMRC_DIR_TX;
            int i;
            for(i=0; i<pac->dlc; ++i)
            {
                sprintf(da+2*i,"%02X", pac->data[i]);
            }
            da[2*i] = 0;

            if((0x00 == type || 0x01 == type) && gEMRCInfo.bCandump_subscribe) //0x00-can; 0x01-canfd
            {
                cJSON *monitor = cJSON_CreateObject();
                cJSON *paras = cJSON_CreateObject();
                cJSON_AddItemToObject(monitor, "cmd", cJSON_CreateString("candump"));
                cJSON_AddItemToObject(monitor, "paras", paras);
                cJSON_AddItemToObject(paras, "id", cJSON_CreateNumber(id));
                cJSON_AddItemToObject(paras, "channel", cJSON_CreateNumber(ch));
                cJSON_AddItemToObject(paras, "direction", cJSON_CreateNumber(direction));
                cJSON_AddItemToObject(paras, "type", cJSON_CreateNumber(type));
                cJSON_AddItemToObject(paras, "dlc", cJSON_CreateNumber(dlc));
                cJSON_AddItemToObject(paras, "data", cJSON_CreateString(da));
                char *dump_str = cJSON_Print(monitor);
                cJSON_Delete(monitor);
                emlog.Info("Send candump to client(%s:%d).",inet_ntoa(gEMRCInfo.client_addr.sin_addr),
                            ntohs(gEMRCInfo.client_addr.sin_port));   
                sendto(gEMRCInfo.sock, dump_str, strlen(dump_str), 0,
                   (struct sockaddr *)&(gEMRCInfo.client_addr), gEMRCInfo.addr_size);
                emlog.Debug("Send message: %s", dump_str);
            }
            else if((type == 0x02) && gEMRCInfo.bLindump_subscribe) 
            {                
                cJSON *monitor = cJSON_CreateObject();
                cJSON *paras = cJSON_CreateObject();
                cJSON_AddItemToObject(monitor, "cmd", cJSON_CreateString("lindump"));
                cJSON_AddItemToObject(monitor, "paras", paras);
                cJSON_AddItemToObject(paras, "id", cJSON_CreateNumber(id));
                cJSON_AddItemToObject(paras, "channel", cJSON_CreateNumber(ch));
                cJSON_AddItemToObject(paras, "direction", cJSON_CreateNumber(direction));                
                cJSON_AddItemToObject(paras, "dlc", cJSON_CreateNumber(dlc));
                cJSON_AddItemToObject(paras, "data", cJSON_CreateString(da));
                char *dump_str = cJSON_Print(monitor);
                cJSON_Delete(monitor);             
                emlog.Info("Send lindump to client(%s:%d).",inet_ntoa(gEMRCInfo.client_addr.sin_addr),
                    ntohs(gEMRCInfo.client_addr.sin_port)); 
                sendto(gEMRCInfo.sock, dump_str, strlen(dump_str), 0,
                   (struct sockaddr *)&(gEMRCInfo.client_addr), gEMRCInfo.addr_size);
                emlog.Debug("Send message: %s", dump_str);
            }
        }
    }
    return;
}

/* support cmd define(follow from: embustest cml):
1.PC->Gcase
.cansend
{
    "cmd":"cansend",     
    "paras":{             
        "channel":"1~15", //表示1~15通道轮流发送,如果只在1通道发则直接填“1”
        "loop":5,       //表示发送的循环次数
        "interval": 1000,   //表示发送的间隔,单位ms
        "frame":"101#std#e#8#ABCD1234CDEF5678" //发送的帧数据:101-canid(十六进制);std-标准帧,fd-canfd帧;e-ide_flag;8-dlc
    }
}

.订阅candump消息
{
    "cmd":"candump_subscribe"
}

.订阅lindump消息
{
    "cmd":"candump_subscribe"
}

2.Gcase->PC
.candump
{
    "cmd":"candump",
    "paras":{
        "id":1,   //canid(十进制)
        "channel": 1,
        "direction":1, //1-send, 0-recv
        "type":"can", //0-can; 1-canfd
        "dlc":8,      //data length,unit:byte
        "data":"ABCD1234CDEF5678"  //data
    }
}

.lindump
{
    "cmd":"lindump",
    "paras":{
        "id":1,
        "channel": 1,
        "direction":1, //1-send, 0-recv        
        "dlc":8,      //data length,unit:byte
        "data":"ABCD1234CDEF5678"  //data
    }
}
*/